Skip to content

feat(github): manage org/repo rulesets declaratively + add v* tag protection#70

Merged
botantler-1[bot] merged 3 commits into
mainfrom
claude/rulesets-config
Jun 25, 2026
Merged

feat(github): manage org/repo rulesets declaratively + add v* tag protection#70
botantler-1[bot] merged 3 commits into
mainfrom
claude/rulesets-config

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Assistant

What & why

The org's GitHub rulesets were the last major piece of org config still managed
only in the UI. This brings them into deploy/ as Crossplane managed resources
(provider-upjet-github), reconciled by the platform github-config tenant like
repositories/, teams/, and labels/ — and adds the tag protection the org
lacked despite releasing on v* tags.

The platform side was already wired for this (the organizationrulesets +
repositoryrulesets MRDs are activated and the tenant RBAC already covers both API
groups); only the manifests were missing.

What's in this PR

Rulesets Policy Risk
organization-rulesets.yaml 10 org rulesets Observe (read-only import) none — Crossplane only mirrors live state
repository-rulesets.yaml ksail Restrict deletions, platform Require merge queue Observe (read-only import) none
tag-protection.yaml Protect release tags (net-new) Managed (Create) the only write — see below

Imports are Observe-first (managementPolicies: ["Observe"]): read-only, zero
behaviour change, zero revert risk — identical to how repos/teams were adopted. Delete
is omitted everywhere, so nothing here can ever delete a real ruleset. external-name binds
each to the existing ruleset (<id> for org, <repo>:<id> for repo).

The one write is Protect release tags: an org tag ruleset over ~ALL repos/tags
that blocks tag deletion + force-move and requires v<semver> names. Creating new
release tags is unaffected; only tampering is blocked.

⚠️ Tier caveat on the tag name pattern: docs flag tag_name_pattern as
"enterprise only" and this org is on the team plan — but it already runs active
org-level branch_name_pattern + commit_message_pattern rulesets, so pattern rules
demonstrably work here. If GitHub nonetheless rejects the ruleset on apply, drop just the
tagNamePattern block (header comment explains) — deletion + nonFastForward still
deliver immutable tags with no tier requirement.

Push / tag / Actions policies (as requested)

Provider gap — 10 of 22 stay UI-managed

provider-upjet-github v0.19.1 can't express repository_property conditions,
code_quality/copilot_code_review rules, or repository-target rulesets, so 10 org
rulesets remain UI-managed (fully active meanwhile). Full matrix in
deploy/rulesets/README.md; re-adoption tracked in #69.

Validation

  • kubectl kustomize deploy/ builds (13 ruleset resources emitted).
  • Every manifest passes kubectl apply --dry-run=server against the live prod CRDs.

Rollout

Draft until you promote it. On merge + a v* release tag of this repo, the artifact
republishes and Crossplane reconciles: the 12 imports become Ready with
status.atProvider populated, and Protect release tags is created. Promoting an import
past Observe to full management is a deliberate, separately-validated follow-up (backfill
forProvider from observed state first — the round-trip is lossy).

…tection

Adopt the org's existing rulesets as Crossplane managed resources under
deploy/rulesets/ (provider-upjet-github), the same Observe-first read-only flow
used for repositories/ and teams/:

- 10 org rulesets -> OrganizationRuleset (Observe), bound by ruleset id
- ksail "Restrict deletions" + platform "Require merge queue"
  -> RepositoryRuleset (Observe), bound by <repo>:<id>
- net-new "Protect release tags" OrganizationRuleset (managed, the only write):
  block tag deletion + force-move + require v<semver> on ~ALL repos/tags

provider-upjet-github v0.19.1 cannot express the other 10 org rulesets
(repository_property conditions, code_quality/copilot_code_review rules,
repository-target rulesets) nor push rulesets nor the new Actions
workflow-execution-protections; these stay UI-managed and are documented in
deploy/rulesets/README.md and tracked in #69.

Validated: kubectl kustomize deploy/ builds; every manifest passes
kubectl apply --dry-run=server against the live CRDs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@devantler devantler marked this pull request as ready for review June 25, 2026 17:50
Per the maintainer's one-resource-per-file preference: split the multi-doc
organization-rulesets.yaml (10) and repository-rulesets.yaml (2) into one file
per resource (filename = resource name, matching ../repositories/), and rename
tag-protection.yaml -> protect-release-tags.yaml to match its resource name.

Behaviour-preserving: the rendered set of 13 ruleset resources is byte-identical
before/after the split (verified via kustomize build diff). kustomization.yaml +
README.md updated for the new layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-verb names

deploy/rulesets/ mixed OrganizationRuleset and RepositoryRuleset. Split into one
subfolder per CR kind, each with an active-verb filename convention:

- organization-rulesets/ — OrganizationRuleset, named after the rule it enforces
  (e.g. require-pull-request.yaml); carries the convention README + importability matrix.
- repository-rulesets/   — RepositoryRuleset as <verb>-on-<repo>.yaml
  (ksail-restrict-deletions -> restrict-deletions-on-ksail;
   platform-require-merge-queue -> require-merge-queue-on-platform).

Pure rename + kustomization/README re-wiring; resource bodies (metadata.name,
crossplane.io/external-name) unchanged. `kubectl kustomize deploy/` validated
(11 OrganizationRuleset, 2 RepositoryRuleset).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
auto-merge was automatically disabled June 25, 2026 18:23

Pull Request is not mergeable

@botantler-1 botantler-1 Bot enabled auto-merge (squash) June 25, 2026 18:23
@botantler-1 botantler-1 Bot merged commit 696d43a into main Jun 25, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant